/**
  ******************************************************************************
  * @file    cam.c
  * @author  Amaseing team
  * @version V1.0.0
  * @date    02-february-2013
  * @brief   Cam
  *         
  ******************************************************************************  
  */ 
  
/* Private includes ----------------------------------------------------------*/ 
#include "cam.h"
#include "dcmi.h" 
#include "sccb.h"
#include "led.h"
#include "ov9655settings.h"

/* Private declarations ------------------------------------------------------*/
image_t image;

/* Private prototypes --------------------------------------------------------*/
/* Private defines ------------------------------------------------------------*/
#define TIMEDELAY 			2000
#define TIMEDELAY_LONG 	16800

/* Functions -----------------------------------------------------------------*/
void Delay(uint32_t nTime)
{
	volatile uint32_t counter = nTime;
	while (counter != 0)
		counter--;
}

void camOV9655sizeSetup(void)
{
	const ov9655reg_t *ptr;
	uint32_t size;
	
#if CAM_WIDTH == (1280)
	ptr = ov9655_yuv_sxga;
	size = sizeof(ov9655_yuv_sxga)/2;
#endif
#if CAM_WIDTH == (640)
	ptr = ov9655_yuv_vga;
	size = sizeof(ov9655_yuv_vga)/2;
#endif
#if CAM_WIDTH == (320)
	ptr = ov9655_yuv_qvga;
	size = sizeof(ov9655_yuv_qvga)/2;
#endif
#if CAM_WIDTH == (160)
	ptr = ov9655_yuv_qqvga;
	size = sizeof(ov9655_yuv_qqvga)/2;
#endif
	
	while(size--)
	{
		sccbWrite(ptr->adr, ptr->val);
		ptr++;
		Delay(TIMEDELAY);
	}
	Delay(TIMEDELAY_LONG);
}

void camInit(void)
{
	uint8_t data = 0;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	Delay(TIMEDELAY_LONG);
	 
	// Init cam clock
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
	
	//Te hoge kloksnelheid zorgt voor frame fouten !!!
	//Daarom voor test op 8Mhz
	
#if CAM_WIDTH  >= (640)
	RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO2Div_4); // 168/4 = 42 MHz.
#else
	RCC_MCO2Config(RCC_MCO2Source_HSE, RCC_MCO2Div_1);  // 8/4 = 8 MHz.
#endif
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_MCO);
	GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_9);
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	// Init cam reset pin
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_1);
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	Delay(TIMEDELAY_LONG);
    
	// Init cam SCCB
	sccbInit();
	
	// Reset cam
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);
	Delay(TIMEDELAY_LONG);
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
	Delay(TIMEDELAY_LONG);
	Delay(TIMEDELAY_LONG);
	
	// Get device ID (part 1) and check
	data = sccbRead(SCCB_MIDH); //=0x7F
	if(data != 0x7F){
		SET_LED(LED_ERROR); // Set fail led
		return;
	}
	// Get device ID (part 2) and check
	data = sccbRead(SCCB_MIDL); //=0xA2
	if(data != 0xA2){
		SET_LED(LED_ERROR); // Set fail led
		return;
	}
	
	// Cam softreset to default settings
	sccbWrite(SCCB_COM7,0x80);
	
	// Delay until cam resets
	Delay(TIMEDELAY_LONG);
	Delay(TIMEDELAY_LONG);
	Delay(TIMEDELAY_LONG);
	
	// Init cam
	camOV9655sizeSetup();
	Delay(TIMEDELAY_LONG);
	
	// Init cam DCMI interface
	dcmiInit(&image);
	Delay(TIMEDELAY_LONG);
	
	//Area from (x,y)(x+x2-1,y+y2-1) of orginal; (0,0)(CAM_WIDTH,CAM_HEIGHT)
	camSetROI(0,512,1280,3); 
	//camSetROI(0,120,640,3); 
	//camSetROI(0,120,320,3); 
	//camSetROI(0,60,160,3); 

	Delay(TIMEDELAY_LONG);
	Delay(TIMEDELAY_LONG);
	Delay(TIMEDELAY_LONG);
	
	camGetFrame();
	camGetFrame();
}
void camSetROI(unsigned int x, unsigned int y, unsigned int width, unsigned int height)
{	
	DCMI_CROPInitTypeDef DCMI_CROPInitStruct;
	
	//non fixable errors
	if(width > CAM_WIDTH)
		return;
	if(height > CAM_HEIGHT)
		return;
	if(x >= CAM_WIDTH)
		return;
	if(y >= CAM_HEIGHT)
		return;
	if(height == 0)
		return;
	if((width%4) != 0)
		return;
		
	//fixable errors
	if(x > (CAM_WIDTH-width))
		x = CAM_WIDTH-width;
	if(y > (CAM_HEIGHT-width))
		y = CAM_HEIGHT-width;

	//Disable crop
	DCMI_CROPCmd(DISABLE); 
	
	//Horizontal, X-direction
	DCMI_CROPInitStruct.DCMI_HorizontalOffsetCount = (x*2);
	//Width: (3,7...), multiple of 4, bits[1:0] are ignored
	//32-bit consist of 2 pixels!
	//CaptureCount = {((8[bit] * 2 * width) / 32[bit] - 1}
	DCMI_CROPInitStruct.DCMI_CaptureCount = ((width*2)-1); 	
	
	//Vertical, Y-direction
	DCMI_CROPInitStruct.DCMI_VerticalStartLine = (y);	
	//VerticalLineCount = (heigth - 1)
	DCMI_CROPInitStruct.DCMI_VerticalLineCount = (height-1);
	
	//Init crop
	DCMI_CROPConfig(&DCMI_CROPInitStruct);
	//Enable crop
	DCMI_CROPCmd(ENABLE);
	
	// Update image meta data
	image.height = height;
	image.width = width;
	image.pixels = (height*width);
}

image_t* camGetFrame(void)
{
	uint16_t check, fail=0;
	
	if(!(DMA2_Stream1->CR & 0x0001)){
		DMA_Cmd(DMA2_Stream1, DISABLE);
		Delay(100);	
		DMA_Cmd(DMA2_Stream1, ENABLE);
	}
	
	DCMI_CaptureCmd(ENABLE);
	do
	{
		Delay(100);
	}
	while(DCMI->CR&0x1); //Wait until the capture is ready
	Delay(100);
	
	// Calculate check
	check = (IMG_MEMORY - ((image.width/2)*image.height));
	
	if(image.check == IMG_MEMORY)
	{
		// Image is not corrupt
		RESET_LED(LED_ERROR); //reset red
	}
	else
	{
		if(image.check == check)
		{
			// Image is not corrupt
			RESET_LED(LED_ERROR); //reset red
		}
		else 
		{
			//Image is corrupt
			SET_LED(LED_ERROR); //set red
			fail = 1;
		}
	}
	
	// Reset DMA; return to start address
	DCMI_Cmd(DISABLE); 
	DMA_Cmd(DMA2_Stream1, DISABLE);
	Delay(100);	
	DMA_Cmd(DMA2_Stream1, ENABLE);
	DCMI_Cmd(ENABLE); 
	
	if(fail) { 
		//restore cam rio to default settings
		camSetROI(0,512,1280,3);
		return NULL;
	}
	return &image;
}
